use crate::dto::ApiResult;
use hypers::hyper::StatusCode;
use hypers::prelude::{hook, Error, Next, Request, Responder, Response};
use hypers::serde_json::json;
use jsonwebtoken::{
    decode, encode, errors::ErrorKind, Algorithm, DecodingKey, EncodingKey, Header, Validation,
};
use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct JWTToken {
    pub id: i32,
    pub username: String,
    pub permissions: Vec<String>,
    aud: String,
    // (audience)：受众
    exp: usize,
    iat: usize,
    // (Issued At)：签发时间
    iss: String,
    // (issuer)：签发人
    nbf: usize,
    // (Not Before)：生效时间
    sub: String,
    // (subject)：主题
    jti: String, // (JWT ID)：编号
}

impl JWTToken {
    pub fn new(id: i32, username: &str, permissions: Vec<String>) -> JWTToken {
        let now = SystemTime::now();
        //过期时间
        let m30 = Duration::from_secs(1800000);
        let now = now.duration_since(UNIX_EPOCH).expect("获取系统时间失败");

        JWTToken {
            id,
            username: String::from(username),
            permissions,
            aud: String::from("rust_admin"), // (audience)：受众
            exp: (now + m30).as_secs() as usize,
            iat: now.as_secs() as usize,     // (Issued At)：签发时间
            iss: String::from("koobe"),      // (issuer)：签发人
            nbf: now.as_secs() as usize,     // (Not Before)：生效时间
            sub: String::from("rust_admin"), // (subject)：主题
            jti: String::from("ignore"),     // (JWT ID)：编号
        }
    }

    /// create token
    /// secret: your secret string
    pub fn create_token(&self, secret: &str) -> Result<String, String> {
        return match encode(
            &Header::default(),
            self,
            &EncodingKey::from_secret(secret.as_ref()),
        ) {
            Ok(t) => Ok(t),
            Err(_) => Err("create token error".to_owned()),
        };
    }
    /// verify token invalid
    /// secret: your secret string
    pub fn verify(secret: &str, token: &str) -> Result<JWTToken, Error> {
        let mut validation = Validation::new(Algorithm::HS256);
        validation.sub = Some("rust_admin".to_string());
        validation.set_audience(&["rust_admin"]);
        validation.set_required_spec_claims(&["exp", "sub", "aud"]);
        return match decode::<JWTToken>(
            &token,
            &DecodingKey::from_secret(secret.as_ref()),
            &validation,
        ) {
            Ok(c) => Ok(c.claims),

            Err(err) => match *err.kind() {
                ErrorKind::InvalidToken => return Err(Error::Response(401, json!("InvalidToken"))), // Example on how to handle a specific error
                ErrorKind::InvalidIssuer => {
                    return Err(Error::Response(401, json!("InvalidIssuer")))
                } // Example on how to handle a specific error
                ErrorKind::ExpiredSignature => {
                    return Err(Error::Response(401, json!("token 已经超时了")))
                } // Example on how to handle a specific error
                // _ => return Err(Error::from("InvalidToken other errors")),
                _ => Err(Error::Response(401, json!("create token error"))),
            },
        };
    }
}

#[hook]
pub async fn auth_token(mut req: Request, next: &mut Next<'_>) -> Result<Response, Error> {
    let mut res: Response = Response::default();
    match req.header::<String>("authorization") {
        None => {
            let resp = ApiResult {
                msg: "token 不能为空".to_owned(),
                code: 1,
                data: Some("None"),
            };
            res.status(StatusCode::UNAUTHORIZED).json(&resp);
            return Ok(res);
        }
        Some(token) => {
            let split_vec = token.split_whitespace().collect::<Vec<_>>();
            if split_vec.len() != 2 || split_vec[0] != "Bearer" {
                let resp = ApiResult {
                    msg: "the token format wrong".to_owned(),
                    code: 1,
                    data: Some("None"),
                };
                res.status(StatusCode::UNAUTHORIZED).json(&resp);
                return Ok(res);
            }
            let token = split_vec[1];
            let jwt_token_e = JWTToken::verify("123", &token);
            let jwt_token = match jwt_token_e {
                Ok(data) => data,
                Err(err) => {
                    let resp = ApiResult {
                        msg: err.to_string(),
                        code: 1,
                        data: Some("None"),
                    };
                    res.status(StatusCode::UNAUTHORIZED).json(&resp);
                    return Ok(res);
                }
            };
            let path = req.uri().path();
            let mut flag: bool = false;
            for token_permission in &jwt_token.permissions {
                if token_permission == path {
                    flag = true;
                    break;
                }
            }
            if flag {
                req.insert("userId", jwt_token.id);
                req.insert("username", jwt_token.username);
                let res = next.next(req).await?;
                return Ok(res);
            } else {
                log::error!("Hi from start. You requested path: {:?}", path);
                let resp = ApiResult {
                    msg: "你没有权限访问".to_owned(),
                    code: 1,
                    data: Some(path),
                };
                res.status(StatusCode::UNAUTHORIZED).json(&resp);
                return Ok(res);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::JWTToken;

    #[test]
    fn test_jwt() {
        let jwt = JWTToken::new(1, "koobe", vec![]);
        let res = jwt.create_token("123");
        println!("{:?}", res);
        let token = JWTToken::verify("123", &res.unwrap_or_default());
        println!("{:?}", token)
    }
}
